package tablaSimbolos;

import java.util.ArrayList;
import java.util.Iterator;

import utils.Clase;
import utils.Tipo;

public class TS {
	private ArrayList<Tupla> tablas;

	public TS() {
		tablas = new ArrayList<Tupla>();
	}

	public TS(ArrayList<Tupla> tablas) {
		setTS(tablas);
	}

	public boolean existeID(String id) {
		boolean encontrado = containsKey(tablas.get(tablas.size() - 1), id);
		for (int i = tablas.size() - 2; !encontrado && i >= 0; i--) {
			encontrado = tablas.get(i).getTS().containsKey(id);
		}
		return encontrado;
	}

	public boolean existeSimbEnUltimoNivel(String id) {
		if (tablas.isEmpty()) {
			return false;
		} else {
			return containsKey(tablas.get(tablas.size() - 1), id);
		}
	}

	public void addSymbol(String iden, Clase clase, Tipo Tipo, Integer dir,
			Integer nivel) {
		boolean encontrado = false;
		Tupla tupla = null;
		for (Iterator<Tupla> it = tablas.iterator(); !encontrado && it.hasNext(); ) {
			tupla = it.next();
			encontrado = nivel == tupla.getNivel();
		}
		while (!encontrado) {
			addNivel();
			tupla = tablas.get(tablas.size() - 1);
			encontrado = nivel == tupla.getNivel();
		}
		tupla.getTS().addSymbol(iden, clase, Tipo, dir);
	}

	public Clase getClase(String id) {
		Tupla tupla = tablas.get(tablas.size() - 1);
		Clase clase = tupla.getTS().getClase(id);
		int nivel = tupla.getNivel();
		for (int i = tablas.size() - 2; clase == null && i >= 0; i--) {
			tupla = tablas.get(i);
			if (tupla.getNivel() < nivel) {
				nivel = tupla.getNivel();
				clase = tupla.getTS().getClase(id);
			}
		}
		return clase;
	}

	public Tipo getTipo(String id) {
		Tupla tupla = tablas.get(tablas.size() - 1);
		Tipo tipo = tupla.getTS().getTipo(id);
		int nivel = tupla.getNivel();
		for (int i = tablas.size() - 2; tipo == null && i >= 0; i--) {
			tupla = tablas.get(i);
			if (tupla.getNivel() < nivel) {
				nivel = tupla.getNivel();
				tipo = tupla.getTS().getTipo(id);
			}
		}
		return tipo;
	}

	public Integer getDir(String id) {
		Tupla tupla = tablas.get(tablas.size() - 1);
		Integer dir = tupla.getTS().getDir(id);
		int nivel = tupla.getNivel();
		for (int i = tablas.size() - 2; dir == null && i >= 0; i--) {
			tupla = tablas.get(i);
			if (tupla.getNivel() < nivel) {
				nivel = tupla.getNivel();
				dir = tupla.getTS().getDir(id);
			}
		}
		return dir;
	}

	public Integer getNivel(String id) {
		Tupla tupla = tablas.get(tablas.size() - 1);
		boolean encontrado = tupla.getTS().containsKey(id);
		Integer nivel = tupla.getNivel();
		for (int i = tablas.size() - 2; !encontrado && i >= 0; i--) {
			tupla = tablas.get(i);
			if (tupla.getNivel() < nivel) {
				nivel = tupla.getNivel();
				encontrado = tupla.getTS().containsKey(id);
			}
		}
		return nivel;
	}

	public ArrayList<Tupla> getTS() {
		return tablas;
	}

	public void setTS(ArrayList<Tupla> tablas) {
		this.tablas = tablas;
	}

	public void addNivel() {
		int nivelMax = -1;
		for (Tupla tupla : tablas) {
			nivelMax = Math.max(nivelMax, tupla.getNivel());
		}
		tablas.add(new Tupla(nivelMax + 1));
	}

	public String toString() {
		return tablas.toString();
	}

	public boolean add(Tupla e) {
		return tablas.add(e);
	}
	
	public Object clone() {
		TS tabla = new TS();
		for (Tupla tupla : tablas) {
			tabla.add((Tupla) tupla.clone());
		}
		return tabla;
	}
	
	public boolean containsKey(Tupla tupla, String id) {
		return tupla.getTS().containsKey(id);
	}

	private class Tupla {

		private TablaSimbolos ts;
		private Integer nivel;

		private Tupla(Integer nivel) {
			this.ts = new TablaSimbolos();
			this.nivel = nivel;
		}

		private Tupla(TablaSimbolos ts, Integer nivel) {
			this.ts = (TablaSimbolos) ts.clone();
			this.ts = ts;
			this.nivel = nivel;
		}

		private TablaSimbolos getTS() {
			return ts;
		}

		private Integer getNivel() {
			return nivel;
		}

		public String toString() {
			return "<ts: " + ts + ", nivel: " + nivel + ">";
		}
		
		public Object clone() {
			return new Tupla((TablaSimbolos) ts.clone(), nivel);
		}
		
	}
}
